/*
        Copyright 2019 Gaurav Kumar

        Licensed under the Apache License, Version 2.0 (the "License");
        you may not use this file except in compliance with the License.
        You may obtain a copy of the License at
        http://www.apache.org/licenses/LICENSE-2.0
        Unless required by applicable law or agreed to in writing, software
        distributed under the License is distributed on an "AS IS" BASIS,
        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        See the License for the specific language governing permissions and
        limitations under the License.
*/

package com.gauravk.bubblenavigation;

import com.gauravk.bubblenavigation.util.AttrUtils;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.*;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.components.element.VectorElement;
import ohos.agp.utils.Color;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.utils.TextAlignment;
import ohos.app.Context;

/**
 * BubbleToggleView
 *
 * @since 2021-04-09
 */
public class BubbleToggleView extends DependentLayout {
    private static final int WIDTH_HEIGHT_ICON = 80;
    private static final int TITLE_SIZE = 14;
    private static final int BADGE_TEXT_SIZE = 8;
    private static final int PADDING_TITLE_LEFT = 8;
    private static final int PADDING_PARENT = 32;

    private static final int SPREAD = 0;
    private static final int INSIDE = 1;
    private static final int PACKED = 2;

    /**
     * 默认动画时长
     */
    private static final int DEFAULT_ANIM_DURATION = 300;

    /**
     * 角标内文字距离组件左右的距离
     */
    private static final int BADGE_PADDING_LEFT_RIGHT = 4;

    /**
     * 自定义title的最长宽度
     */
    private static final int MAX_TITLE_WIDTH = 300;

    /**
     * 随机定义一个icon的id
     */
    private static final int ID_ICON = 327946546;

    private BubbleToggleItem bubbleToggleItem;
    private DependentLayout mSecondaryDependentLayout;

    private AnimatorValue mCurAnimator;

    /**
     * 是否被选中
     */
    private boolean isActive = false;

    private Image iconView;
    private Text titleView;
    private Text badgeView;

    /**
     * 动画时长
     */
    private int animationDuration;

    /**
     * 自定义title当前宽度
     */
    private int measuredTitleWidth;

    /**
     * 显示的模式
     * 0：默认，两层嵌套
     * 1：三层嵌套，中间一层用于具体显示背景
     * 2：背景不执行执行动画
     */
    private int modeDisplay = SPREAD;

    /**
     * 构造函数
     *
     * @param context 上下文
     */
    public BubbleToggleView(Context context) {
        super(context);
        init(context, null);
    }

    /**
     * 构造函数
     *
     * @param context 上下文
     * @param attrs   自定义属性
     */
    public BubbleToggleView(Context context, AttrSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    /**
     * 构造函数
     *
     * @param context      上下文
     * @param attrs        自定义属性
     * @param styleName    样式名称
     */
    public BubbleToggleView(Context context, AttrSet attrs, String styleName) {
        super(context, attrs, styleName);
        init(context, attrs);
    }

    /**
     * Initialize
     *
     * @param context current context
     * @param attrs   custom attributes
     */
    private void init(Context context, AttrSet attrs) {
        initAttrSet(context, attrs);

        isActive = AttrUtils.getBooleanValueByAttr(attrs, "bt_active", false);
        animationDuration = AttrUtils.getIntValueByAttr(attrs, "bt_duration", DEFAULT_ANIM_DURATION);

        String modeStr = AttrUtils.getStringValueByAttr(attrs, "bt_modeDisplay", "spread");
        switch (modeStr) {
            case "spread":
                modeDisplay = SPREAD;
                break;
            case "inside":
                modeDisplay = INSIDE;
                break;
            case "packed":
                modeDisplay = PACKED;
                break;
            default:
                modeDisplay = SPREAD;
                break;
        }

        // set the gravity
        setGravity(LayoutAlignment.CENTER);

        // set the internal padding
        if (modeDisplay == SPREAD || modeDisplay == PACKED) {
            setPadding(
                    bubbleToggleItem.getInternalPadding(),
                    bubbleToggleItem.getInternalPadding(),
                    bubbleToggleItem.getInternalPadding(),
                    bubbleToggleItem.getInternalPadding());
        }
        createBubbleItemView(context);
        setInitialState(isActive);
    }

    private void initAttrSet(Context context, AttrSet attrs) {
        Element icon = AttrUtils.getElement(attrs, "bt_icon",
                new VectorElement(context, ResourceTable.Graphic_default_icon));
        Element iconSel = AttrUtils.getElement(attrs, "bt_sel_icon",
                new VectorElement(context, ResourceTable.Graphic_default_icon));
        int iconWidth = AttrUtils.getIntValueByAttr(attrs, "bt_iconWidth", WIDTH_HEIGHT_ICON);
        int iconHeight = AttrUtils.getIntValueByAttr(attrs, "bt_iconHeight", WIDTH_HEIGHT_ICON);
        Element shape = AttrUtils.getElement(attrs, "bt_shape",
                new ShapeElement(context, ResourceTable.Graphic_default_shape_bg));
        Element shapeRect = AttrUtils.getElement(attrs, "bt_shapeRect",
                new ShapeElement(context, ResourceTable.Graphic_default_shape_bg));
        String title = AttrUtils.getStringValueByAttr(attrs, "bt_title", "Title");
        int titleSize = AttrUtils.getIntValueByAttr(attrs, "bt_titleSize", TITLE_SIZE);
        Color colorActive = AttrUtils.getColorValueByAttr(attrs, "bt_colorActive",
                new Color(getContext().getColor(ResourceTable.Color_default_active_color)));
        Color colorInactive = AttrUtils.getColorValueByAttr(attrs, "bt_colorInactive",
                new Color(getContext().getColor(ResourceTable.Color_default_inactive_color)));
        int internalPadding = AttrUtils.getIntValueByAttr(attrs, "bt_padding", PADDING_PARENT);
        int titlePadding = AttrUtils.getIntValueByAttr(attrs, "bt_titlePadding", PADDING_TITLE_LEFT);
        int badgeTextSize = AttrUtils.getIntValueByAttr(attrs, "bt_badgeTextSize", BADGE_TEXT_SIZE);
        Color badgeBackgroundColor = AttrUtils.getColorValueByAttr(attrs,
                "bt_badgeBackgroundColor",
                new Color(getContext().getColor(ResourceTable.Color_default_badge_background_color)));
        Color badgeTextColor = AttrUtils.getColorValueByAttr(attrs, "bt_badgeTextColor",
                new Color(getContext().getColor(ResourceTable.Color_default_badge_text_color)));
        String badgeText = AttrUtils.getStringValueByAttr(attrs, "bt_badgeText", null);

        // set the default icon
        if (icon == null) {
            icon = new VectorElement(context, ResourceTable.Graphic_default_icon);
        }

        // set the default shape
        if (shape == null) {
            shape = new ShapeElement(context, ResourceTable.Graphic_transition_background_drawable);
        }

        // create default bubble item
        bubbleToggleItem = new BubbleToggleItem();
        bubbleToggleItem.setIcon(icon);
        bubbleToggleItem.setIconSel(iconSel);
        bubbleToggleItem.setShape(shape);
        bubbleToggleItem.setShapeRect(shapeRect);
        bubbleToggleItem.setTitle(title);
        bubbleToggleItem.setTitleSize(titleSize);
        bubbleToggleItem.setTitlePadding(titlePadding);
        bubbleToggleItem.setColorActive(colorActive);
        bubbleToggleItem.setColorInactive(colorInactive);
        bubbleToggleItem.setIconWidth(iconWidth);
        bubbleToggleItem.setIconHeight(iconHeight);
        bubbleToggleItem.setInternalPadding(internalPadding);
        bubbleToggleItem.setBadgeText(badgeText);
        bubbleToggleItem.setBadgeBackgroundColor(badgeBackgroundColor);
        bubbleToggleItem.setBadgeTextColor(badgeTextColor);
        bubbleToggleItem.setBadgeTextSize(badgeTextSize);
    }

    /**
     * Create the components of the bubble item view {@link #iconView} and {@link #titleView}
     *
     * @param context current context
     */
    private void createBubbleItemView(Context context) {
        // create the nav icon
        iconView = new Image(context);
        iconView.setId(ID_ICON);
        LayoutConfig lpIcon = new LayoutConfig(bubbleToggleItem.getIconWidth(), bubbleToggleItem.getIconHeight());
        lpIcon.addRule(LayoutConfig.VERTICAL_CENTER, LayoutConfig.TRUE);
        lpIcon.addRule(LayoutConfig.ALIGN_PARENT_START, LayoutConfig.TRUE);
        iconView.setLayoutConfig(lpIcon);
        if (isActive) {
            iconView.setBackground(bubbleToggleItem.getIconSel());
        } else {
            iconView.setBackground(bubbleToggleItem.getIcon());
        }

        // create the nav title
        titleView = new Text(context);
        LayoutConfig lpTitle = new LayoutConfig(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT);
        lpTitle.addRule(LayoutConfig.VERTICAL_CENTER, LayoutConfig.TRUE);
        lpTitle.addRule(LayoutConfig.END_OF, iconView.getId());
        titleView.setLayoutConfig(lpTitle);
        titleView.setMultipleLine(false);
        titleView.setTextColor(bubbleToggleItem.getColorActive());
        titleView.setText(bubbleToggleItem.getTitle());
        titleView.setTextSize(bubbleToggleItem.getTitleSize(), Text.TextSizeType.FP);

        // get the current measured title width
        titleView.setVisibility(VISIBLE);

        // update the margin of the text view
        titleView.setPadding(bubbleToggleItem.getTitlePadding(), 0, bubbleToggleItem.getTitlePadding(), 0);

        // measure the content width
        titleView.estimateSize(0, 0);

        // get width
        measuredTitleWidth = titleView.getEstimatedWidth();

        // limit measured width, based on the max width
        if (measuredTitleWidth > MAX_TITLE_WIDTH) {
            measuredTitleWidth = MAX_TITLE_WIDTH;
        }

        // change the visibility
        titleView.setVisibility(HIDE);

        // 不同情况下的布局显示
        if (modeDisplay == SPREAD || modeDisplay == PACKED) {
            addComponent(iconView);
            addComponent(titleView);
        } else {
            // create the DependentLayout
            mSecondaryDependentLayout = new DependentLayout(context);
            mSecondaryDependentLayout.setLayoutConfig(
                    new LayoutConfig(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT));

            mSecondaryDependentLayout.setPadding(
                    bubbleToggleItem.getInternalPadding(),
                    bubbleToggleItem.getInternalPadding(),
                    bubbleToggleItem.getInternalPadding(),
                    bubbleToggleItem.getInternalPadding());

            mSecondaryDependentLayout.addComponent(iconView);
            mSecondaryDependentLayout.addComponent(titleView);
            addComponent(mSecondaryDependentLayout);
        }
        updateBadge(context);

        // set the initial state
        setInitialState(isActive);
    }

    /**
     * Adds or removes the badge
     * @param context context
     */
    private void updateBadge(Context context) {
        // remove the previous badge view
        if (badgeView != null) {
            removeComponent(badgeView);
        }

        if (bubbleToggleItem.getBadgeText() == null) {
            return;
        }

        // create badge
        badgeView = new Text(context);
        LayoutConfig lpBadge = new LayoutConfig(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT);
        lpBadge.addRule(LayoutConfig.ALIGN_TOP, iconView.getId());
        lpBadge.addRule(LayoutConfig.ALIGN_END, iconView.getId());
        badgeView.setLayoutConfig(lpBadge);
        badgeView.setMultipleLine(false);
        badgeView.setTextColor(bubbleToggleItem.getBadgeTextColor());
        badgeView.setText(bubbleToggleItem.getBadgeText());
        badgeView.setTextSize(bubbleToggleItem.getBadgeTextSize(), Text.TextSizeType.FP);
        badgeView.setTextAlignment(TextAlignment.CENTER);
        ShapeElement drawable = new ShapeElement(context, ResourceTable.Graphic_default_background_badge);
        badgeView.setBackground(drawable);

        // update the margin of the text view
        badgeView.setPadding(BADGE_PADDING_LEFT_RIGHT, 0, BADGE_PADDING_LEFT_RIGHT, 0);

        // measure the content width
        badgeView.estimateSize(0, 0);
        if (badgeView.getEstimatedWidth() < badgeView.getEstimatedHeight()) {
            badgeView.setWidth(badgeView.getEstimatedHeight());
        }
        addComponent(badgeView);
    }

    /**
     * Updates the Initial State
     *
     * @param isActiveState current state
     */
    public void setInitialState(boolean isActiveState) {
        if (isActiveState) {
            // set the background
            if (modeDisplay == INSIDE) {
                mSecondaryDependentLayout.setBackground(bubbleToggleItem.getShapeRect());
            } else {
                setBackground(bubbleToggleItem.getShapeRect());
            }
            this.isActive = true;
            titleView.setVisibility(VISIBLE);
        } else {
            // set the background
            if (modeDisplay == INSIDE) {
                mSecondaryDependentLayout.setBackground(bubbleToggleItem.getShape());
            } else {
                setBackground(bubbleToggleItem.getShape());
            }
            this.isActive = false;
            titleView.setVisibility(HIDE);
        }
    }

    /**
     * Toggles between Active and Inactive state
     */
    public void toggle() {
        if (!isActive) {
            activate();
        } else {
            deactivate();
        }
    }

    /**
     * Set Active state
     */
    public void activate() {
        if(mCurAnimator == null || !mCurAnimator.isRunning()){
            isActive = true;

            mCurAnimator = new AnimatorValue();
            mCurAnimator.setDuration(animationDuration);
            final boolean[] isFirstDisplay = {true};
            mCurAnimator.setValueUpdateListener((animatorValue, v) -> {
                titleView.setWidth((int) (measuredTitleWidth * v));

                if (isFirstDisplay[0]) {
                    titleView.setVisibility(VISIBLE);
                    isFirstDisplay[0] = false;
                }
            });
            mCurAnimator.start();
            if (modeDisplay == INSIDE) {
                mSecondaryDependentLayout.setBackground(bubbleToggleItem.getShapeRect());
            } else {
                setBackground(bubbleToggleItem.getShapeRect());
            }
            iconView.setBackground(bubbleToggleItem.getIconSel());
        }
    }

    /**
     * Set Inactive State
     */
    public void deactivate() {
        if(mCurAnimator == null || !mCurAnimator.isRunning()){
            isActive = false;
            mCurAnimator = new AnimatorValue();
            mCurAnimator.setDuration(animationDuration);
            mCurAnimator.setValueUpdateListener((animatorValue, value) -> {
                titleView.setWidth((int) (measuredTitleWidth * (1 - value)));

                // end of animation
                if (value >= 1.0f) {
                    titleView.setVisibility(HIDE);
                    if (modeDisplay == SPREAD) {
                        setBackground(bubbleToggleItem.getShape());
                    } else if (modeDisplay == INSIDE) {
                        mSecondaryDependentLayout.setBackground(bubbleToggleItem.getShape());
                    }
                }
            });
            mCurAnimator.start();
            if (modeDisplay == PACKED) {
                setBackground(bubbleToggleItem.getShape());
            }
            iconView.setBackground(bubbleToggleItem.getIcon());
        }
    }

    /**
     * Get the current state of the view
     *
     * @return the current state
     */
    public boolean isActive() {
        return isActive;
    }

    /**
     * Updates the measurements and fits the view
     *
     * @param maxWidth in pixels
     */
    public void updateMeasurements(int maxWidth) {
        int marginLeft = 0;
        int marginRight = 0;

        ComponentContainer.LayoutConfig titleViewLayoutParams = titleView.getLayoutConfig();
        if (titleViewLayoutParams != null) {
            marginLeft = titleViewLayoutParams.getMarginLeft();
            marginRight = titleViewLayoutParams.getMarginRight();
        }

        int newTitleWidth = maxWidth
                - (getPaddingRight() + getPaddingLeft())
                - (marginLeft + marginRight)
                - ((int) bubbleToggleItem.getIconWidth())
                + titleView.getPaddingRight() + titleView.getPaddingLeft();

        // if the new calculate title width is less than current one, update the titleView specs
        if (newTitleWidth > 0 && newTitleWidth < measuredTitleWidth) {
            measuredTitleWidth = titleView.getEstimatedWidth();
        }
    }

    /**
     * Set value to the Badge's
     *
     * @param value as String, null to hide
     */
    public void setBadgeText(String value) {
        bubbleToggleItem.setBadgeText(value);
        updateBadge(getContext());
    }

    /**
     * 判断是否可点击
     *
     * @return 是否可点击
     */
    public boolean isCanClick(){
        return mCurAnimator == null || !mCurAnimator.isRunning();
    }
}
